package com.android.stockme.backend.fetcher;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.android.stockme.backend.configuration.IConfigurationChangedListener;
import com.android.stockme.backend.configuration.IConfigurationManager;
import com.android.stockme.backend.util.Tupple;



public class GoogleDataFetcher implements IDataFetcher {
	

	/**
	 * @uml.property   name="newsManager"
	 * @uml.associationEnd   multiplicity="(1 1)" inverse="googleDataFetcher:com.android.stockme.backend.fetcher.INewsManager"
	 */
	private INewsManager newsManager = null;
	
	/**
	 * @uml.property   name="historyManager"
	 * @uml.associationEnd   multiplicity="(1 1)" inverse="googleDataFetcher:com.android.stockme.backend.fetcher.IHistoryManager"
	 */
	private IHistoryManager historyManager = null;
	
	private Collection<String> symbols = null;

	private IConfigurationManager configurationManager = null;
	
	private IConfigurationChangedListener stockAddedListener = null;
	
	private IConfigurationChangedListener stockRemovedListener = null;
	
	public GoogleDataFetcher(IConfigurationManager configurationManager)
	{
		this.historyManager = new GoogleHistoryManager();
		this.newsManager = new GoogleNewsManager();
		this.symbols = new HashSet<String>();
		this.configurationManager = configurationManager;
		
		for (String s : this.configurationManager.getStockSymbols())
			this.symbols.add(s);

		this.stockAddedListener = new IConfigurationChangedListener() {
			public void notifyConfigurationChanged(Object arg) {
				synchronized (symbols) {
					symbols.add((String)arg);					
				}
			}
		};
		this.stockRemovedListener = new IConfigurationChangedListener() {
			public void notifyConfigurationChanged(Object arg) {
				synchronized (symbols) {
					symbols.remove((String)arg);					
				}
				historyManager.removeStockFromCache((String)arg);				
			}
		};
			
		this.configurationManager.registerStockAddedListener(this.stockAddedListener);
		this.configurationManager.registerStockRemovedListener(this.stockRemovedListener);

	}//GoogleDataFetcher


	@Override
	protected void finalize() throws Throwable {
		configurationManager.unregisterStockAddedListener(this.stockAddedListener);
		configurationManager.unregisterStockRemovedListener(this.stockRemovedListener);
		
		super.finalize();
	}


	public Map<String, Tupple<Float, Float>> fetchStocksData() {	

		StringBuffer buf = new StringBuffer();
		
		synchronized (this.symbols) {
			if (this.symbols.size()==0)
				return null;		
		}
		
		buf.append("http://www.google.com/finance/info?client=ig&q=");
		
		synchronized (this.symbols) {
			for(String symbol : this.symbols){
				buf.append(symbol);
				buf.append(",");
			}	
		}
		
        try{
            //URL for the stocks quote from Google! service
        	URL url = new URL(buf.toString());

            //Set the timeouts for connection and read
            URLConnection connection = url.openConnection();
            connection.setConnectTimeout(1000);
            connection.setReadTimeout(1000);
            InetAddress.getAllByName("www.google.com");//check if we have connection
            
            //establish connection
            BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
            StringBuffer htmlPage = new StringBuffer();;
            String line;
			while ((line = in.readLine()) != null){
                htmlPage.append(line);
            }//get the html page
			
            in.close();
            if ((null == htmlPage) || (htmlPage.toString().length() <= 1)){
                return null;
            }
            // Populate stock values
            Map<String, Tupple<Float, Float>> map = new HashMap<String, Tupple<Float, Float>>();
        	String symbol;
        	Float stockValue;
        	Float stockRelChange;
        	Tupple<Float, Float> valAndRelChange;
        	
            Pattern symbolPatern = Pattern.compile("\"t\" : \"([^\"]*)\"");
            Pattern valuePatern = Pattern.compile("\"l\" : \"[$]?([^\"]*)[$]?\"");
            Pattern relChangePatern = Pattern.compile("\"cp\" : \"([^\"]*)\"");
            Matcher symbolMatcher = symbolPatern.matcher(htmlPage);
            Matcher valueMatcher = valuePatern.matcher(htmlPage);
            Matcher relChangeMatcher = relChangePatern.matcher(htmlPage);
            
            while ((symbolMatcher.find()) && (valueMatcher.find()) && (relChangeMatcher.find())){
            	
            	try{
            		symbol = symbolMatcher.group(1);
                	stockValue = Float.valueOf(valueMatcher.group(1).trim());
                	stockRelChange = Float.valueOf(relChangeMatcher.group(1).trim());
            	}catch (NumberFormatException ex) {
                    ex.printStackTrace();
                    continue;
                }

            	valAndRelChange = new Tupple<Float, Float>(stockValue ,  stockRelChange);
            	map.put(symbol, valAndRelChange);
            }

            synchronized (this.symbols) {
        		for(String badSymbol : this.symbols){
        			if (!map.containsKey(badSymbol))
        				map.put(badSymbol, new Tupple<Float, Float>(null ,  null));
        		}				
			}
            
            if (0 == map.size())
            	return null;
            
            return map;
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        
        return null;
	}//fetchStocksData

	
	public List<Tupple<String, Float>> fetchStockHistory(String symbol) {
		
		return this.historyManager.fetchHistory(symbol);
		
	}//fetchStockHistory

	/**
	 * Getter of the property <tt>historyManager</tt>
	 * @return  Returns the historyManager.
	 * @uml.property  name="historyManager"
	 */
	public IHistoryManager getHistoryManager() {
		
		return historyManager;
		
	}//getHistoryManager

	/**
	 * Setter of the property <tt>historyManager</tt>
	 * @param historyManager  The historyManager to set.
	 * @uml.property  name="historyManager"
	 */
	public void setHistoryManager(IHistoryManager historyManager) {
		
		this.historyManager = historyManager;
		
	}//setHistoryManager



	/**
	 * Getter of the property <tt>newsManager</tt>
	 * @return  Returns the newsManager.
	 * @uml.property  name="newsManager"
	 */
	public INewsManager getNewsManager() {
		
		return newsManager;
		
	}//getNewsManager

	
	
	/**
	 * Setter of the property <tt>newsManager</tt>
	 * @param newsManager  The newsManager to set.
	 * @uml.property  name="newsManager"
	 */
	public void setNewsManager(INewsManager newsManager) {
		
		this.newsManager = newsManager;
		
	}//setNewsManager
	
	

	public List<INewsItem> fetchNewsUpdates() {

		Collection<String> symbols_cloned = new HashSet<String>();
		
		synchronized (this.symbols) {
			if (0 == this.symbols.size())
				return null;
			for (String s : this.symbols)
				symbols_cloned.add(new String(s));
		}
		
		return this.newsManager.fetchNewsUpdate(symbols_cloned);
		
	}//fetchNewsUpdates
	

}//GoogleDataFetcher class
